package jmine.tec.utils.reflection;

import java.lang.reflect.GenericArrayType;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;

/**
 * Resolve tipos genéricos se possível. Resolver o tipo significa devolver pelo menos o limite superior caso o tipo formal não esteja
 * completamente especificado.
 * <p>
 * Nenhum metodo pode receber <code>null</code> como parametro.
 */
public interface GenericTypeResolver {

    /**
     * Verifica se o tipo generico passado pode ser resolvido para um tipo - se ele está fixo
     * 
     * @param genericType o tipo genérico
     * @param declaringClass o tipo que declara a classe
     * @return boolean
     */
    boolean isResolvable(Type genericType, Class<?> declaringClass);

    /**
     * Resolve o i-ésimo tipo genérico declarado pelo {@link ParameterizedType} dado como uma classe. Qualquer tentativa falha resultara em
     * uma {@link IllegalArgumentException}.
     * <p>
     * 
     * @param anyType o {@link ParameterizedType} que descreve os tipos genéricos da classe.
     * @param containingClass a classe "folha" que se quer investigar. Esta folha deve, pelo menos, definir limites superiores para os
     * parametros formais do {@link ParameterizedType}.
     * @param i o indice do parametro do {@link ParameterizedType} que se quer resolver.
     * @return {@link Class}
     */
    Class<?> resolveTypeForParameterizedType(ParameterizedType anyType, Class<?> containingClass, int i);

    /**
     * Resolve o {@link TypeVariable} declarado em uma classe a partir da subclasse passada como parametro. A {@link TypeVariable}
     * representa o tipo generico declarado um uma superclasse da classe passada. Este método resolve transitividade entre tipos formais -
     * em outras palavras, situações como:
     * <p>
     * <code>
     * class C1&lt;E&gt; { E getE() {...} }
     * class C2&lt;F&gt; extends C1&lt;F&gt;{}
     * class C3 extends C1&lt;String&gt;{}
     * </code>
     * <p>
     * Se qualquer etapa do processo falha, este método lança uma {@link IllegalArgumentException}
     * 
     * @param type o parametro generico desejado
     * @param containingClass a classe que 'fixa'
     * @return Class
     */
    Class<?> resolveTypeForFixedGenericType(TypeVariable<?> type, Class<?> containingClass);

    /**
     * Resolve o tipo dos componentes de um array, desde que este esteja fixo na classe passada.
     * 
     * @param type o {@link GenericArrayType}
     * @param containingClass a classe que fixa pelo menos um 'upper bound' no tipo generico
     * @return Class<?>
     */
    Class<?> resolveComponentTypeForFixedGenericArrayType(GenericArrayType type, Class<?> containingClass);

}
